
/*
 * Copyright (C) 2008-2009 Archie L. Cobbs. All rights reserved.
 *
 * $Id: BeanProperty.java 301 2010-01-18 22:56:09Z archie.cobbs $
 */

package org.dellroad.sidekar.annotation;

import java.beans.Introspector;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Represents a non-private Java bean property.
 */
class BeanProperty {

    private final String name;
    private final Method readMethod;
    private final Method writeMethod;

    /**
     * Constructor.
     *
     * @param readMethod property getter
     * @param writeMethods set of possible setter methods
     */
    public BeanProperty(Method readMethod, Set<Method> writeMethods) {

        // Check access
        if ((readMethod.getModifiers() & Modifier.PRIVATE) != 0)
            throw new IllegalArgumentException("getter method is private");

        // Check name
        Matcher getterMatcher = Pattern.compile("(get|is)(\\p{javaJavaIdentifierPart}+)").matcher(readMethod.getName());
        if (!getterMatcher.matches()
          || (getterMatcher.group(1).equals("is") && readMethod.getReturnType() != boolean.class))
            throw new IllegalArgumentException("invalid getter name");

        // Check return type
        Type type = readMethod.getGenericReturnType();
        if (type == void.class)
            throw new IllegalArgumentException("getter has void return type");

        // Check parameters
        if (readMethod.getParameterTypes().length != 0)
            throw new IllegalArgumentException("getter must not take any parameters");

        // Find associated write method, if any
        Method writeMethodFound = null;
        for (Method method : writeMethods) {
            if (method.getReturnType() != void.class)
                continue;
            Type[] ptypes = method.getParameterTypes();
            if (ptypes.length != 1 || !ptypes[0].equals(type))
                continue;
            Matcher setterMatcher = Pattern.compile("set(\\p{javaJavaIdentifierPart}+)").matcher(method.getName());
            if (!setterMatcher.matches() || !setterMatcher.group(1).equals(getterMatcher.group(2)))
                continue;
            if ((method.getModifiers() & Modifier.PRIVATE) != 0)
                throw new IllegalArgumentException("setter method is private");
            writeMethodFound = method;
            break;
        }

        // Done
        this.name = Introspector.decapitalize(getterMatcher.group(2));
        this.readMethod = readMethod;
        this.writeMethod = writeMethodFound;
    }

    public String getName() {
        return this.name;
    }

    public Type getType() {
        return this.readMethod.getGenericReturnType();
    }

    public Method getReadMethod() {
        return this.readMethod;
    }

    public Method getWriteMethod() {
        return this.writeMethod;
    }
}

